/*
 * @Author: wuqingchun
 * @Date: 2024-04-15 13:59:54
 * @Description: 智能指针的使用
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-04-16 22:38:29
 */

#include <iostream>
#include <memory>

#include "my_unique_ptr.h"
#include "my_lock_guard.h"
#include "my_shared_ptr.h"

/**
 * @brief 智能指针模板类
 * 利用C++析构函数会被自动调用的特性，将资源裸指针用 C++ 类管理起来，避免内存泄漏问题
 * 内存泄漏问题可能有主观遗漏释放和客观上无法释放两个原因产生
 * @tparam T 
 */
template <typename T>
class SmartPointer
{
private:
    /* data */
    T* m_pObjPtr {nullptr};

public:
    SmartPointer(T* pObjPtr)
    {
        m_pObjPtr = pObjPtr;
    }

    ~SmartPointer()
    {
        if (m_pObjPtr != nullptr)
        {
            delete m_pObjPtr;
            m_pObjPtr = nullptr;

            std::cout << "release resource" << std::endl;
        }
    }

    T& operator* ()
    {
        return *m_pObjPtr;
    }

    T* operator-> ()
    {
        return m_pObjPtr;
    }

    T& operator[](size_t ulPos)
    {
        return m_pObjPtr[ulPos];
    }
};


int div()
{
    int iDividend, iDivisor;
    std::cin >> iDividend >> iDivisor;

    if (iDivisor == 0)
    {
        throw std::invalid_argument("Division by zero error");
    }

    return iDividend / iDivisor;
}

void test_memory_leak()
{
    int* piValue = new int;
    *piValue = div();

    std::cout << "result: " << *piValue << std::endl;

    delete piValue;
}

void test_memory_leak2()
{
    int* piValue = new int;

    try
    {
        /* code */
        *piValue = div();
    }
    catch(const std::exception& e)
    {
        delete piValue;
        std::cerr << e.what() << '\n';
        return;
    }
    
    std::cout << "result: " << *piValue << std::endl;

    delete piValue;
}

void test_memory_leak3()
{
    int* piValue = new int;
    SmartPointer<int> objSP(piValue);

    try
    {
        /* code */
        *piValue = div();
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
        return;
    }
    
    std::cout << "result: " << *piValue << std::endl;
}

void test_memory_leak4()
{
    SmartPointer<int> objSP(new int);

    try
    {
        /* code */
        *objSP = div();
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
        return;
    }
    
    std::cout << "result: " << *objSP << std::endl;
}

void test_unique_ptr()
{
    std::cout << "=====test_unique_ptr begin====" << std::endl;

    std::unique_ptr<int> objUP(new int(10));
    std::cout << *objUP << std::endl;
    //std::unique_ptr<int> objUP2 = objUP;

    // 不能复制，但是可以通过 std::move 移动所有权
    std::unique_ptr<int> objUP3 = std::move(objUP);
    // 移动后不能使用，进程段错误
    //std::cout << *objUP << std::endl;
    std::cout << *objUP3 << std::endl;

    // 指定删除器
    std::unique_ptr<int, void(*)(int*)> objUP4(new int(100), [](int* p){std::cout << "Custom deleter" << std::endl; delete p;});
    std::cout << *objUP4 << std::endl;

    std::cout << "release: " << objUP4.release() << std::endl;
    std::cout << "get: " << objUP4.get() << std::endl;

    std::cout << "=====test_unique_ptr end====" << std::endl;
}

void test_my_unique_ptr()
{
    std::cout << "=====test_my_unique_ptr begin====" << std::endl;

    my_unique_ptr<int> objUP(new int(20));
    std::cout << *objUP << std::endl;
    // 删除了拷贝构造函数，禁用复制
    //my_unique_ptr<int> objUP2 = objUP;

    // 不能复制，但是可以通过 std::move 移动所有权
    my_unique_ptr<int> objUP3 = std::move(objUP);
    // 移动后不能使用，进程段错误
    //std::cout << *objUP << std::endl;
    std::cout << *objUP3 << std::endl;

    // 指定删除器
    my_unique_ptr<int, void(*)(int*)> objUP4(new int(200), [](int* p){std::cout << "Custom deleter" << std::endl; delete p;});
    std::cout << *objUP4 << std::endl;

    std::cout << "release: " << objUP4.release() << std::endl;
    std::cout << "get: " << objUP4.get() << std::endl;

    std::cout << "=====test_my_unique_ptr end====" << std::endl;
}

void test_shared_ptr()
{
    std::cout << "=====test_shared_ptr begin====" << std::endl;
    std::shared_ptr<int> objSP(new int(30), [](int* p){std::cout << "Custom shared_ptr deleter" << std::endl; delete p;});
    std::cout << *objSP << std::endl;

    std::shared_ptr<int> objSP1(objSP);
    std::cout << *objSP1 << std::endl;

    objSP.get();
    objSP1.get();
    
    objSP.reset();
    objSP1.reset();

    std::cout << "=====test_shared_ptr end====" << std::endl;
}

void test_my_shared_ptr()
{
    std::cout << "=====test_my_shared_ptr begin====" << std::endl;
    my_shared_ptr<int> objSP(new int(30));
    std::cout << *objSP << std::endl;

    my_shared_ptr<int> objSP1(objSP);
    std::cout << *objSP1 << std::endl;

    objSP.get();
    objSP1.get();
    
    objSP.reset();
    objSP1.reset();

    std::cout << "=====test_my_shared_ptr end====" << std::endl;
}

int main(int argc, char** argv)
{
    //test_memory_leak();
    //test_memory_leak2();
    //test_memory_leak3();
    test_memory_leak4();

    test_unique_ptr();
    test_my_unique_ptr();

    test_shared_ptr();

    TestLockGuard objTest;
    objTest.Test();

    test_my_shared_ptr();

    return 0;
}